package com.example.springbooteasycode.zjrc.project.OuterMidProd.command40;

import com.example.springbooteasycode.zjrc.ConversionUtils;

import java.util.Arrays;

/**
 * 模拟CSMP-Outer模块的40指令-MAC计算(核心国密)
 */
// 模拟 C 中的结构体，用于和外部交互数据（对应 StruHsmData ）
class StruHsmDataFinally {
    byte[] dataBuffer = new byte[1024]; // 假设足够大的缓冲区，根据实际调整
    int dataLen;
}

// 模拟 C 中的 SndUnit 结构体（如果有相关功能需要用到时可扩展，当前代码未完整用到其所有逻辑）
class SndUnitFinally {
    byte[] bData = new byte[1024];
    int dwLen;
}

// 常量定义（如果有其他常量可继续补充）
class ConstantFinally {
    static final int DKRY_LEN = 16;
    static final byte[] FIXED_BYTES = {(byte) 0xE1, 0x10, 0x00, (byte) 0xFF, (byte) 0xFF, 0x10, (byte) 0x05};
    static final char[] ascii_table = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
}
public class ASm4MacUtilsFinally {

    public static void cmd40(byte[] lpIn, SndUnitFinally sndUn) {
        // 解析长度，从 lpIn 前两个字节获取，类似 C 中 (in[0] << 8) | in[1]
        int len = ((lpIn[0] & 0xFF) << 8) | (lpIn[1] & 0xFF);
        byte[] macbuf = new byte[16];
        byte[] buf = new byte[8];

        // 模拟密钥数据，实际应替换为真实密钥
        // todo---实际的密钥，从 kdc密钥管理服务中获取，国密MAK密钥
        byte[] key = new byte[16];
        String keyMAK = "4886e2d2772c95225dfb5b9490878dfe";
        key = hexStringToByteArray(keyMAK);
        // 在 Java 中，Arrays.copyOfRange()是java.util.Arrays类提供的一个静态方法，用于将指定数组的指定范围的元素复制到一个新的数组中。
        /**
         * lpIn：是源字节数组，即要从中复制元素的数组。
         * 2：表示复制的起始索引（包含该索引位置的元素），意味着从lpIn数组的索引为 2 的元素开始复制。
         * lpIn.length：表示复制的结束索引（不包含该索引位置的元素），也就是复制到lpIn数组的最后一个元素的前一个位置。
         */
        // lpIn数组的前两个字节用于解析数据长度，而genMAC_SM4()方法需要除前两个字节之外的数据，
        // 所以Arrays.copyOfRange(lpIn, 2, lpIn.length) 就是把lpIn数组中从索引2开始到末尾的所有元素复制到一个新的字节数组中，
        // 并将这个新数组作为参数传递给genMAC_SM4()方法 ，以获取处理数据时真正需要的数据部分。
        int ret = genMAC_SM4(key, len, Arrays.copyOfRange(lpIn, 2, lpIn.length), macbuf);
        if (ret != 0) {
            System.err.println("GenMAC_SM4 error, ret code: " + ret);
            return;
        }

        // 调用 hex2str 函数，将 macbuf 转换为字符串并存入 buf（示例取前 4 个字节转换，可按需调整）
        byte[] writeBuf = new byte[8];
        hex2str(macbuf, writeBuf, 4, 0);

        // 模拟 memcpy 操作，将数据拷贝到发送缓冲区（这里假设 termId 等数据需填充，示例用空数据演示流程）
        byte[] termId = new byte[8];
        String termIdStr = "00013320";
        termId = termIdStr.getBytes();
        System.arraycopy(termId, 0, sndUn.bData, 0, 8);
        System.arraycopy(writeBuf, 0, sndUn.bData, 8, 8);
        sndUn.dwLen = 16;

    }

    public static int genMAC_SM4(byte[] mak, int datalen, byte[] data, byte[] mac) {
        /**
         * 对应c中的初始化 定义
         */
        StruHsmDataFinally sndData = new StruHsmDataFinally();
        StruHsmDataFinally rcvData = new StruHsmDataFinally();
        int lpIndex = 0;

        // 初始化数据结构，类似 C 中的 memset
        Arrays.fill(sndData.dataBuffer, (byte) 0);
        sndData.dataLen = 0;
        Arrays.fill(rcvData.dataBuffer, (byte) 0);
        rcvData.dataLen = 0;

        // 填充固定字节和相关数据，类似 C 中对 sndData.dataBuffer 的填充逻辑
        for (byte b : ConstantFinally.FIXED_BYTES) {
            sndData.dataBuffer[lpIndex++] = b;
        }
        System.arraycopy(mak, 0, sndData.dataBuffer, lpIndex, ConstantFinally.DKRY_LEN);
        // 打印sndData.dataBuffer的十六进制字符串形式，只要有数据的部分
        // todo- 00E11000FFFF10654886E2D2772C95225DFB5B9490878DFE
        System.out.println("sndData.dataBuffer: " + bytesToHexString(Arrays.copyOfRange(sndData.dataBuffer, 0, lpIndex)));
//        System.out.println("sndData.dataBuffer: " + bytesToHexString(sndData.dataBuffer));
        lpIndex += ConstantFinally.DKRY_LEN;

        // 追加16位的"00000000000000000000000000000000"初始化向量
        // 初始化 iv 数组（对应 C 中的 unsigned char iv[16] = {0};）
        byte[] iv = new byte[16];
        Arrays.fill(iv, (byte) 0);
        int wlen = 7 + ConstantFinally.DKRY_LEN; // 对应 C 中的 wlen 计算逻辑

        int len = 0;
        // ===================================while方法内======================================
        while (datalen > 0) {
            int currentLen;

            // 核心调整：将iv拷贝移至datalen判断之前，与C代码逻辑一致
            // 拷贝iv到发送缓冲区（对应C中的memcpy(lp, iv, 16)）
            System.arraycopy(iv, 0, sndData.dataBuffer, lpIndex, 16);
            lpIndex += 16;


            // todo- 处理大于1024的数据
            if (datalen > 1024) {
                currentLen = 1024;
                // 处理 datalen 大于 1024 的情况，设置两个字节表示长度（高字节和低字节）
                sndData.dataBuffer[lpIndex++] = (byte) ((currentLen >> 8) & 0xFF);
                sndData.dataBuffer[lpIndex++] = (byte) (currentLen & 0xFF);
                // 复制对应长度的数据到 sndData.dataBuffer
                System.arraycopy(data, len, sndData.dataBuffer, lpIndex, currentLen);
                lpIndex += currentLen;
                sndData.dataLen = wlen + 16 + 2 + currentLen;
                datalen -= currentLen;
                len += currentLen;
            } else {
                // todo-处理<1024的数据mac
                currentLen = datalen;
                // 处理 datalen 小于等于 1024 的情况，同样设置两个字节表示长度
                sndData.dataBuffer[lpIndex++] = (byte) ((currentLen >> 8) & 0xFF);
                sndData.dataBuffer[lpIndex++] = (byte) (currentLen & 0xFF);
                // 打印sndData.dataBuffer的十六进制字符串形式
                System.out.println("sndData.dataBuffer: " + bytesToHexString(Arrays.copyOfRange(sndData.dataBuffer, 0, lpIndex)));
//                System.out.println("sndData.dataBuffer: " + bytesToHexString(Arrays.copyOf(sndData.dataBuffer, sndData.dataLen)));
//                System.out.println("sndData.dataBuffer: " + bytesToHexString(sndData.dataBuffer));

                System.arraycopy(data, len, sndData.dataBuffer, lpIndex, currentLen);
                lpIndex += currentLen;
                sndData.dataLen = wlen + 16 + 2 + currentLen;
                datalen = 0;
                len += currentLen;

                // 模拟 CommHsm 交互 - 打印发送数据（使用实际返回的十六进制字符串演示，可替换为真实 Socket 交互）
//                System.out.println("Simulating CommHsm request, sending data: " + bytesToHexString(sndData.dataBuffer));
//                System.out.println("Simulating CommHsm request, sending data: " +
//                        Arrays.toString(Arrays.copyOf(sndData.dataBuffer, sndData.dataLen)));
                System.out.println("Simulating CommHsm request, sending data: " +
                        bytesToHexString(Arrays.copyOf(sndData.dataBuffer, sndData.dataLen)));

            }

            // todo-实际的socket请求
            // 实际返回的十六进制字符串（示例值，可替换为真实返回）
            String hexResponse = "4159589ddc0507e660846b4575c0ddcd8c";
            // 将十六进制字符串转换为字节数组
            byte[] responseBytes = hexStringToByteArray(hexResponse);

            // 填充接收缓冲区
            Arrays.fill(rcvData.dataBuffer, (byte) 0);
            rcvData.dataLen = responseBytes.length;
            System.arraycopy(responseBytes, 0, rcvData.dataBuffer, 0, responseBytes.length);
            // 只打印有效长度
            System.out.println("Simulating CommHsm response, response data: " + bytesToHexString(Arrays.copyOf(rcvData.dataBuffer, rcvData.dataLen)));
//            System.out.println("Simulating CommHsm response, response data: " +bytesToHexString(rcvData.dataBuffer));

            // 检查返回状态
            int ret = 0;
            if (ret != 0) {
                return ret;
            }

            // 从返回数据更新iv（对应C中的memcpy(iv, &rcvData.dataBuffer[1], 16)）
            System.arraycopy(rcvData.dataBuffer, 1, iv, 0, 16);

        }
        // ===================================while方法内======================================

        // 提取MAC结果
        // Java 代码中添加 Math.min(16, rcvData.dataLen) 是为了模拟 C 语言中隐含的边界安全逻辑，避免数组越界。
        // 如果 rcvData.dataLen < 16（比如返回数据异常），直接拷贝 16 字节会触发 ArrayIndexOutOfBoundsException。Math.min(16, rcvData.dataLen) 会自动取较小值，避免崩溃。
        System.arraycopy(iv, 0, mac, 0, Math.min(16, rcvData.dataLen));
        // 打印十六进制字符串mac
        System.out.println("mac: " + bytesToHexString(mac));

        return 0;
    }

    // hex2str 函数实现，将字节数组转换为对应的十六进制字符串存入目标数组
    public static void hex2str(byte[] read, byte[] write, int len, int writeIndex) {
        int readIndex = 0;
        while (len-- > 0 && readIndex < read.length && writeIndex + 1 < write.length) {
            hex2asc(read[readIndex++], write, writeIndex);
            writeIndex += 2;
        }
    }

    // hex2asc 函数实现，将一个字节转换为两个十六进制字符存入目标数组
    public static void hex2asc(byte dat, byte[] write, int writeIndex) {
        int highNibble = (dat & 0xF0) >> 4;
        write[writeIndex] = (byte) ConstantFinally.ascii_table[highNibble];
        int lowNibble = dat & 0x0F;
        write[writeIndex + 1] = (byte) ConstantFinally.ascii_table[lowNibble];
    }

    public static void main(String[] args) {
        // 简单测试示例，需根据实际完善参数
        // >1024。
//        String hexIn = "0E3632333039313031393935353439";
        // <1024
        String hexIn = "00083131313131313145";
        byte[] lpIn = hexStringToByteArray(hexIn);
        SndUnitFinally sndUn = new SndUnitFinally();
        cmd40(lpIn, sndUn);
        System.out.println("Final send data: " + Arrays.toString(Arrays.copyOf(sndUn.bData, sndUn.dwLen)));
        // 只打印有效长度的 sndUn.bData 数据
        System.out.println("Final bData: " + bytesToHexString(Arrays.copyOf(sndUn.bData, sndUn.dwLen)));
//        System.out.println("Final bData: " + bytesToHexString(sndUn.bData));
    }

    // 十六进制字符串转字节数组
    private static byte[] hexStringToByteArray(String s) {
        int len = s.length();
        byte[] data = new byte[len / 2];
        for (int i = 0; i < len; i += 2) {
            data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4) +
                    Character.digit(s.charAt(i + 1), 16));
        }
        return data;
    }

    // 字节数组 → 十六进制字符串
    public static String bytesToHexString(byte[] bytes) {
        StringBuilder result = new StringBuilder();
        for (byte b : bytes) {
            result.append(String.format("%02X", b));
        }
        return result.toString();
    }


}
